
package storage

import (
	"sync"

	"github.com/coreos/go-semver/semver"
	"go.etcd.io/etcd/raft/v3/raftpb"
	"go.etcd.io/etcd/server/v3/etcdserver/api/snap"
	"go.etcd.io/etcd/server/v3/storage/wal"
	"go.etcd.io/etcd/server/v3/storage/wal/walpb"
	"go.uber.org/zap"
)

type Storage interface {
	// Save（）方法负责将 Entry 记录和 HardState 状态信息保存到底层的持久化存储上，
	// 该方法可能会阻塞， 在后面的介绍中会看到，
	// Storage 接 口 的实现是通过 WAL 模块将上述数据持久化到 WAL 日志文件中的
	Save(st raftpb.HardState, ents []raftpb.Entry) error
	// SaveSnap （）方法负责将快照数据持久到底层的持久化存储上，该方法也可能会阻塞，
	// Storage 接口的实现是使用 Snapshotter 将快照数据保存到快照文件中的
	SaveSnap(snap raftpb.Snapshot) error
	// Close关闭存储并执行终结。
	Close() error
	// Release释放比提供的快照早的锁定wal文件。
	Release(snap raftpb.Snapshot) error
	// 同步WAL 
	Sync() error
	// 最小ETCDVersion返回能够解释WAL日志的最小etcd存储。
	MinimalEtcdVersion() *semver.Version
}

type storage struct {
	lg *zap.Logger
	s  *snap.Snapshotter

	// 互斥保护变量
	mux sync.RWMutex
	w   *wal.WAL
}

func NewStorage(lg *zap.Logger, w *wal.WAL, s *snap.Snapshotter) Storage {
	return &storage{lg: lg, w: w, s: s}
}

// SaveSnap将快照文件保存到磁盘并写入WAL快照条目。
// 保存的 HardState 及待持久化 Entry 记录
func (st *storage) SaveSnap(snap raftpb.Snapshot) error {
	st.mux.RLock()
	defer st.mux.RUnlock()
	walsnap := walpb.Snapshot{	// 根据快照的元数据创建对应的 walpb.Snapshot 实例
		Index:     snap.Metadata.Index,
		Term:      snap.Metadata.Term,
		ConfState: &snap.Metadata.ConfState,
	}
	// 通过 Snapshotter ~年快照数据写入到破盘，
	err := st.s.SaveSnap(snap)
	if err != nil {
		return err
	}
	// 根据 WAL 日志文件的名称及快照的元数据，将放快照之前的 WAL 日 志文件句柄
	return st.w.SaveSnapshot(walsnap)
}

// Release释放比给定快照早且不再需要的资源：
// -释放对比给定快照提供的wal早的wal文件的锁。
// /-删除任何。断裂早于给定快照的db文件。
func (st *storage) Release(snap raftpb.Snapshot) error {
	st.mux.RLock()
	defer st.mux.RUnlock()
	if err := st.w.ReleaseLockTo(snap.Metadata.Index); err != nil {
		return err
	}
	return st.s.ReleaseSnapDBs(snap)
}

func (st *storage) Save(s raftpb.HardState, ents []raftpb.Entry) error {
	st.mux.RLock()
	defer st.mux.RUnlock()
	return st.w.Save(s, ents)
}

func (st *storage) Close() error {
	st.mux.Lock()
	defer st.mux.Unlock()
	return st.w.Close()
}

func (st *storage) Sync() error {
	st.mux.RLock()
	defer st.mux.RUnlock()
	return st.w.Sync()
}

func (st *storage) MinimalEtcdVersion() *semver.Version {
	st.mux.Lock()
	defer st.mux.Unlock()
	walsnap := walpb.Snapshot{}

	sn, err := st.s.Load()
	if err != nil && err != snap.ErrNoSnapshot {
		panic(err)
	}
	if sn != nil {
		walsnap.Index = sn.Metadata.Index
		walsnap.Term = sn.Metadata.Term
		walsnap.ConfState = &sn.Metadata.ConfState
	}
	w, err := st.w.Reopen(st.lg, walsnap)
	if err != nil {
		panic(err)
	}
	_, _, ents, err := w.ReadAll()
	if err != nil {
		panic(err)
	}
	v := wal.MinimalEtcdVersion(ents)
	st.w = w
	return v
}
